/**
 * Copyright 2018 人人开源 http://www.renren.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.tang.common.aspect;

import com.google.gson.Gson;
import com.tang.common.annotation.SysLog;
import com.tang.modules.sys.entity.SysLogEntity;
import com.tang.modules.sys.entity.SysUserEntity;
import com.tang.modules.sys.service.SysLogService;
import com.tang.util.HttpContextUtil;
import com.tang.util.IPUtil;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 系统日志，切面处理类
 * 注意的是@After和@AfterReturning或者有多个@After和@AfterReturning如果同时存在于一个文件里，
 * 他们的执行顺序是从上到下依次执行的；还有就是这里面只有@Around可以修改目标方法的接收参数和返回值，
 * 其他的都不可以修改；还有就是可以使用@AfterThrowing来对目标方法抛出的异常进行捕获和操作。
 * 详情请查看https://www.jianshu.com/p/f1fd510a57c8
 *
 * @author Mark 15062230055@163.com
 * @since 1.3.0 201803-08
 */
@Aspect
@Component
public class SysLogAspect {

    @Autowired
    private SysLogService sysLogService;

    @Pointcut("@annotation(com.tang.common.annotation.SysLog)")
    public void logPointCut() {
    }

    /**
     * ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中
     * joinPoint用于获取目标方法相关信息的参数
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed(); //执行目标方法
        // Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存系统日志
        saveSysLog(point, time);
        return result;
    }

    /**
     * 保存系统日志
     *
     * @param joinPoint
     * @param time
     */
    private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLogEntity sysLog = new SysLogEntity();
        SysLog syslog = method.getAnnotation(SysLog.class);
        if (syslog != null) {
            //注解上的描述（用户操作描述）
            sysLog.setOperation(syslog.value());
        }
        // 获取目标类的信息
        String className = joinPoint.getTarget().getClass().getName();
        // 请求的方法名
        String methodName = signature.getName();
        sysLog.setMethod(className + "." + methodName + "()");
        // 请求的参数，获取传入目标方法的参数对象
        Object[] args = joinPoint.getArgs();
        try {
            String params = new Gson().toJson(args[0]);
            sysLog.setParams(params);
        } catch (Exception e) {

        }
        //获取request
        HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
        //设置IP地址
        sysLog.setIp(IPUtil.getIpAddr(request));
        //用户名
        String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
        sysLog.setUsername(username);
        sysLog.setTime(time);
        sysLog.setCreateDate(new Date());
        //保存系统日志
        sysLogService.insert(sysLog);
    }

    /**
     * 与After的区别在于AfterReturning只有在方法执行成功的之后才会被织入，如果After和
     * AfterReturning同时存在于一个文件中，谁写在前面谁先运行
     * @param joinPoint
     * @param rvt方法的返回值
     */
	/*@AfterReturning(pointcut="execution(* com.service.*.*(..))", returning="rvt")
	public void log(JoinPoint joinPoint, Object rvt) {
		System.out.println("AfterReturning增强：获取目标方法的返回值：" + rvt);
		System.out.println("AfterReturning增强：模拟日志功能");
		System.out.println("AfterReturning增强：获织入增强的目标方法为：" + joinPoint.getSignature().getName());
		System.out.println("AfterReturning增强：目标方法的参数为：" + Arrays.toString(joinPoint.getArgs()));
		System.out.println("AfterReturning增强：被织入增强处理的目标对象为：" + joinPoint.getTarget());
	}*/

}
